package ca.kooki.codesnippet;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import ca.kooki.codesnippet.core.TagCloud;
import ca.kooki.codesnippet.core.User;

public class StatisticSummary {

  
  private static final int MAX_TOP_POSTERS_TO_SHOW = 5;
  
  //```````````````````````
  // MEMBER VARIABLES
  //```````````````````````
  
  private Map<User,Integer> _allTopPostersMap;
  private TagCloud _cloud;
  
  private int _numberOfSnippets;
  private int _maxSnippetLength;
  private int _minSnippetLength;
  private int _averageSnippetLength;
  
  private List<String> _allSnippets;
  
  
  //```````````````````````
  // CONSTRUCTOR
  //```````````````````````

  public StatisticSummary()
  {
    init();
  }
  
  private void init()
  {
    _allTopPostersMap = new Hashtable<User,Integer>();
    _allSnippets = new ArrayList<String>();
    _numberOfSnippets = 0;
    _maxSnippetLength = 0;
    _minSnippetLength = 0;
    _averageSnippetLength = 0;
    _cloud = new TagCloud();
  }
 
  
  //```````````````````````
  // INTERFACE
  //```````````````````````
  
  public void addTag(Tag tag)
  {
    User user = tag.getUser();
    addTopPoster(user,1);
    addKeywords(tag.getKeywordList());
    
    if (tag.getCodeSnippet().isOfficial())
    {
      String id = tag.getCodeSnippet().getId();
      if (_allSnippets.contains(id))
      {
        return;
      }
      else
      {
        _allSnippets.add(id);
      }
    }
    
    
    int newTagLength = tag.getCodeSnippet().getCode().length();
    int newSumOfTagLengths = _averageSnippetLength*_numberOfSnippets + newTagLength;
    _numberOfSnippets+=1;
    _averageSnippetLength = newSumOfTagLengths / _numberOfSnippets;
    
    _maxSnippetLength = Math.max(_maxSnippetLength, newTagLength);
    _minSnippetLength = _minSnippetLength == 0 ? newTagLength : Math.min(_minSnippetLength, newTagLength);
    
    
  }
  
  public void addKeywords(String[] allKeywords)
  {
    _cloud.addKeywords(allKeywords);
  }
  
  public void addTopPoster(User user, int numberOfPosts)
  {
    Integer currentNumberOfPosts = _allTopPostersMap.get(user);
    Integer newPosts = currentNumberOfPosts == null ? new Integer(numberOfPosts) : new Integer(currentNumberOfPosts.intValue() + numberOfPosts);
    _allTopPostersMap.put(user, newPosts);
  }
  
  public User[] topPosters()
  {
    
    ArrayList<Map.Entry> sortedMap = new ArrayList<Map.Entry>( _allTopPostersMap.entrySet() );
    
    Collections.sort( sortedMap , new Comparator<Map.Entry>() {
        public int compare( final Map.Entry e1 , final Map.Entry e2 )
        {
            final Integer first = (Integer)e1.getValue();
            final Integer second = (Integer)e2.getValue();
            return second.compareTo(first);
        }
    });
    
    User[] sortedUsers = new User[sortedMap.size()];
    
    int index = 0;
    Iterator iterator = sortedMap.iterator();
    while (iterator.hasNext() )
    {
      Map.Entry entry = (Map.Entry)iterator.next();
      sortedUsers[index++] = (User)entry.getKey();
    }
    return sortedUsers;
  }
  
  public int numberOfPostsFor(String username)
  {
    return numberOfPostsFor(new User(username));
  }
  
  public int numberOfPostsFor(User user)
  {
    Integer count = _allTopPostersMap.get(user);
    return count == null ? 0 : count.intValue();
  }
  
  public void addCodeSnippetLengths(int size, int average, int max, int min)
  {
    _numberOfSnippets = size;
    _averageSnippetLength = average;
    _maxSnippetLength = max;
    _minSnippetLength = min;
  }
  
  public int numberOfCodeSnippets()
  {
    return _numberOfSnippets;
  }
  
  public int averageCodeSnippetLength()
  {
    return _averageSnippetLength;
  }

  public int maximumCodeSnippetLength()
  {
    return _maxSnippetLength;
  }

  public int minimumCodeSnippetLength()
  {
    return _minSnippetLength;
  }
  
  public TagCloud getTagCloud()
  {
    return _cloud;
  }
  
  public String toHtml()
  {
    StringBuilder builder = new StringBuilder();
    
    builder.append("<html><head>");
    builder.append("<style>");
    builder.append(".smallestTag { font-size: xx-small; }");
    builder.append(".smallTag { font-size: small; }");
    builder.append(".mediumTag { font-size: medium; }");
    builder.append(".largeTag { font-size: large; }");
    builder.append(".largestTag { font-size: xx-large; }"); 
    builder.append("</style>");
    builder.append("</head><body>");
    
    User[] allTopPosters = topPosters();
    builder.append("<h2>Top Posters</h2>");
    builder.append("<table>");
    for(int i=0; i<allTopPosters.length && i<MAX_TOP_POSTERS_TO_SHOW; i++)
    {
      User poster = allTopPosters[i];
      builder.append("<tr><td>"+ poster.getName() +"</td><td>"+ numberOfPostsFor(poster) +"</td></tr>");
    }
    builder.append("</table>");

    builder.append("<h2>Code Snippets</h2>");
    builder.append("<table>");
    builder.append("<tr><td>Average length</td><td>"+ averageCodeSnippetLength() +"</td></tr>");
    builder.append("<tr><td>Longest Snippet</td><td>"+ maximumCodeSnippetLength() +"</td></tr>");
    builder.append("<tr><td>Shortest Snippet</td><td>"+ minimumCodeSnippetLength() +"</td></tr>");
    builder.append("<tr><td>Number of Entries</td><td>"+ numberOfCodeSnippets() +"</td></tr>");
    builder.append("</table>");

    
    String [] allKeywords = _cloud.getCloud();
    builder.append("<h2>Keyword Density</h2>");
    builder.append("<div>");
    for(int i=0; i<allKeywords.length; i++)
    {
      String keyword = allKeywords[i];
      String magnitudeClass = getClassFrom(_cloud.getMagnitudeOf(keyword));
      builder.append("<span class='"+ magnitudeClass +"'>"+ keyword +"</span> ");
    }
    builder.append("</div>");
    builder.append("</body></html>");
    
    
    return builder.toString();
  }

  
  //```````````````````````
  // PRIVATE METHODS
  //```````````````````````
  
  private String getClassFrom(TagCloud.Magnitude magnitude)
  {
    return magnitude.toString().toLowerCase() + "Tag";
  }
}
